package com.harmontronics.hanma.paas.paas.domain.app.service;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.harmontronics.hanma.paas.paas.common.PageBase;
import com.harmontronics.hanma.paas.paas.consts.NumberEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.HmModelPublish;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.EntityModelStatusEnum;
import com.harmontronics.hanma.paas.paas.domain.app.entity.valueobject.I18nKeyEnum;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmEntityModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmModelPublishRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.persistence.HmPublishedEntityModelRepository;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmEntityModelPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmModelPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmModelPublishPO;
import com.harmontronics.hanma.paas.paas.domain.app.repository.po.HmPublishedEntityModelPO;
import com.harmontronics.hanma.paas.paas.exception.ErrorKeyException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author ldb
 * @since v1.1-sprint1
 */
@Service("hmModelPublishService")
@DS("#header.App-Unique")
public class HmModelPublishService {

    @Autowired
    private HmModelPublishRepository hmModelPublishRepository;

    @Autowired
    private HmModelRepository hmModelRepository;

    @Autowired
    private HmEntityModelRepository hmEntityModelRepository;

    @Autowired
    private HmPublishedEntityModelRepository hmPublishedEntityModelRepository;


    public HmModelPublish findById(Long id) {
        HmModelPublishPO po = hmModelPublishRepository.getById(id);
        return BeanUtil.toBean(po, HmModelPublish.class);
    }

    public void save(HmModelPublish hmModelPublish) {
        HmModelPublishPO po = BeanUtil.toBean(hmModelPublish, HmModelPublishPO.class);
        hmModelPublishRepository.save(po);
    }

    public void update(HmModelPublish hmModelPublish) {
        HmModelPublishPO po = BeanUtil.toBean(hmModelPublish, HmModelPublishPO.class);
        hmModelPublishRepository.updateById(po);
    }


    public List<HmModelPublish> getList(Long modelId) {

        List<HmModelPublishPO> pos = hmModelPublishRepository.findByModelId(modelId);
        return Convert.toList(HmModelPublish.class, pos);
    }

    public PageBase<HmModelPublish> getList(int pageNo, int pageSize) {
        Page<HmModelPublishPO> page = new Page<>(pageNo, pageSize);
        IPage<HmModelPublishPO> resultPage =
                hmModelPublishRepository.selectPage(Wrappers.emptyWrapper(), page);
        return PageBase.from(resultPage, HmModelPublish.class);
    }


    @Transactional(rollbackFor = Exception.class)
    public Long byScript(Long id, boolean isActivated, String remark) {
        List<HmEntityModelPO> scripts = getScriptEntityModels(id);

        List<HmModelPublishPO> publishPOS = hmModelPublishRepository.findByModelId(id);
        Integer version = getNextVersion(publishPOS);

        HmModelPublishPO publishPO = makeModelPublishPO(isActivated, remark, id, version);
        if (isActivated) {
            //修改模型当前版本
            HmModelPO hmModelPO = hmModelRepository.getById(id);
            hmModelPO.setVersion(version);
            hmModelRepository.updateById(hmModelPO);
            //之前的激活版本置为非激活状态
            publishPOS.stream().filter(e -> e.getActivated() == NumberEnum.ONE.getNum()).forEach(e -> {
                e.setActivated(NumberEnum.ZERO.getNum());
                hmModelPublishRepository.updateById(e);
            });
        }
        List<HmEntityModelPO> builtIns = hmEntityModelRepository.findBuiltIn(id);

        List<HmEntityModelPO> allFields = new ArrayList<>();
        allFields.addAll(scripts);
        allFields.addAll(builtIns);

        int size = allFields.size();
        for (int i = 0; i < size; i++) {
            HmEntityModelPO po = allFields.get(i);
            HmPublishedEntityModelPO publishedEntityModelPO = new HmPublishedEntityModelPO();
            publishedEntityModelPO.setModelId(id);
            publishedEntityModelPO.setPublishId(publishPO.getId());
            publishedEntityModelPO.setEntityModelId(po.getId());
            publishedEntityModelPO.setSerialNum(i + 1);
            hmPublishedEntityModelRepository.save(publishedEntityModelPO);
        }
        scripts.forEach(e->e.setStatus(EntityModelStatusEnum.PUBLISHED));
        hmEntityModelRepository.updateBatchById(scripts);
        return publishPO.getId();
    }

    private Integer getNextVersion(List<HmModelPublishPO> publishPOS) {

        if (publishPOS.isEmpty()) {
            return 1;
        }
        return publishPOS.stream().mapToInt(HmModelPublishPO::getVersion).max().getAsInt() + 1;
    }
    @Transactional(rollbackFor = Exception.class)
    public Long byPublish(boolean isActivated, String remark, Long publishId) {
        HmModelPublishPO basePublishPo = hmModelPublishRepository.getById(publishId);
        List<HmEntityModelPO> scripts = getScriptEntityModels(basePublishPo.getModelId());

        List<HmModelPublishPO> publishPOS = hmModelPublishRepository.findByModelId(basePublishPo.getModelId());
        Integer version = getNextVersion(publishPOS);

        HmModelPublishPO publishPO = makeModelPublishPO(isActivated, remark, basePublishPo.getModelId(), version);
        if (isActivated) {
            //修改模型当前版本
            HmModelPO hmModelPO = hmModelRepository.getById(basePublishPo.getModelId());
            hmModelPO.setVersion(version);
            hmModelRepository.updateById(hmModelPO);
            //之前的激活版本置为非激活状态
            publishPOS.stream().filter(e -> e.getActivated() == NumberEnum.ONE.getNum()).forEach(e -> {
                e.setActivated(NumberEnum.ZERO.getNum());
                hmModelPublishRepository.updateById(e);
            });
        }
        List<HmPublishedEntityModelPO> basePublishedEntityModels = hmPublishedEntityModelRepository.findByPublishId(publishId);
        List<HmPublishedEntityModelPO> targetPos = new ArrayList<>();
        for (HmEntityModelPO po : scripts) {
            HmPublishedEntityModelPO publishedEntityModelPO = new HmPublishedEntityModelPO();
            publishedEntityModelPO.setModelId(basePublishPo.getModelId());
            publishedEntityModelPO.setPublishId(publishPO.getId());
            publishedEntityModelPO.setEntityModelId(po.getId());
            targetPos.add(publishedEntityModelPO);
        }
        for (HmPublishedEntityModelPO basePo : basePublishedEntityModels) {
            HmPublishedEntityModelPO publishedEntityModelPO = new HmPublishedEntityModelPO();
            publishedEntityModelPO.setModelId(basePo.getModelId());
            publishedEntityModelPO.setPublishId(publishPO.getId());
            publishedEntityModelPO.setEntityModelId(basePo.getEntityModelId());
            targetPos.add(publishedEntityModelPO);
        }
        int size = targetPos.size();
        for (int i = 0; i < size; i++) {
            HmPublishedEntityModelPO publishedEntityModelPO = targetPos.get(i);
            publishedEntityModelPO.setSerialNum(i + 1);
            hmPublishedEntityModelRepository.save(publishedEntityModelPO);
        }
        scripts.forEach(e->e.setStatus(EntityModelStatusEnum.PUBLISHED));
        hmEntityModelRepository.updateBatchById(scripts);
        return publishPO.getId();
    }

    public void activate(Long id) {
        HmModelPublishPO basePublishPo = hmModelPublishRepository.getById(id);
        List<HmModelPublishPO> publishPOS = hmModelPublishRepository.findByModelId(basePublishPo.getModelId());
        //修改模型当前版本
        HmModelPO hmModelPO = hmModelRepository.getById(basePublishPo.getModelId());
        hmModelPO.setVersion(basePublishPo.getVersion());
        hmModelRepository.updateById(hmModelPO);
        //之前的激活版本置为非激活状态
        publishPOS.stream().filter(e -> e.getActivated() == NumberEnum.ONE.getNum()).forEach(e -> {
            e.setActivated(NumberEnum.ZERO.getNum());
            hmModelPublishRepository.updateById(e);
        });
        basePublishPo.setActivated(NumberEnum.ONE.getNum());
        hmModelPublishRepository.updateById(basePublishPo);
    }

    public Long byMultiVersion(boolean isActivated, String remark, List<Long> entityModelIds,Long modelId) {
        List<HmEntityModelPO> scripts = getScriptEntityModels(modelId);
        List<HmModelPublishPO> publishPOS = hmModelPublishRepository.findByModelId(modelId);
        Integer version = getNextVersion(publishPOS);

        HmModelPublishPO publishPO = makeModelPublishPO(isActivated, remark, modelId, version);
        if (isActivated) {
            //修改模型当前版本
            HmModelPO hmModelPO = hmModelRepository.getById(modelId);
            hmModelPO.setVersion(version);
            hmModelRepository.updateById(hmModelPO);
            //之前的激活版本置为非激活状态
            publishPOS.stream().filter(e -> e.getActivated() == NumberEnum.ONE.getNum()).forEach(e -> {
                e.setActivated(NumberEnum.ZERO.getNum());
                hmModelPublishRepository.updateById(e);
            });
        }
        List<HmPublishedEntityModelPO> targetPos = new ArrayList<>();
        for (HmEntityModelPO po : scripts) {
            HmPublishedEntityModelPO publishedEntityModelPO = new HmPublishedEntityModelPO();
            publishedEntityModelPO.setModelId(modelId);
            publishedEntityModelPO.setPublishId(publishPO.getId());
            publishedEntityModelPO.setEntityModelId(po.getId());
            targetPos.add(publishedEntityModelPO);
        }
        List<HmEntityModelPO> builtIns = hmEntityModelRepository.findBuiltIn(modelId);
        List<Long> builtInIds = builtIns.stream().map(HmEntityModelPO::getId).collect(Collectors.toList());
        entityModelIds.removeAll(builtInIds);
        entityModelIds.addAll(new ArrayList<>(builtInIds));
        for (Long entityModelId : entityModelIds) {
            HmPublishedEntityModelPO publishedEntityModelPO = new HmPublishedEntityModelPO();
            publishedEntityModelPO.setModelId(modelId);
            publishedEntityModelPO.setPublishId(publishPO.getId());
            publishedEntityModelPO.setEntityModelId(entityModelId);
            targetPos.add(publishedEntityModelPO);
        }
        int size = targetPos.size();
        for (int i = 0; i < size; i++) {
            HmPublishedEntityModelPO publishedEntityModelPO = targetPos.get(i);
            publishedEntityModelPO.setSerialNum(i + 1);
            hmPublishedEntityModelRepository.save(publishedEntityModelPO);
        }
        scripts.forEach(e->e.setStatus(EntityModelStatusEnum.PUBLISHED));
        hmEntityModelRepository.updateBatchById(scripts);
        return publishPO.getId();
    }

    private HmModelPublishPO makeModelPublishPO(boolean isActivated, String remark, Long modelId, Integer version) {
        HmModelPublishPO publishPO = new HmModelPublishPO();
        publishPO.setModelId(modelId);
        publishPO.setRemark(remark);
        publishPO.setVersion(version);
        publishPO.setActivated(isActivated ? 1 : 0);
        hmModelPublishRepository.save(publishPO);
        return publishPO;
    }

    private List<HmEntityModelPO> getScriptEntityModels(Long modelId) {
        List<HmEntityModelPO> scripts = hmEntityModelRepository.findScript(modelId);
        if (scripts.isEmpty()) {
            throw new ErrorKeyException(I18nKeyEnum.SCRIPT_FIELD_NOT_FOUND);
        }
        return scripts;
    }
}